﻿using CodeMonkey.EditableObject.Descriptor;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Reflection;
using System.Text;
using CommonLib.ExtendMethods;

namespace CodeMonkey.EditableObject.Core
{
    [Obsolete("不再建议使用,改用CodeMonkey.EditableObject.Core.EditableDynamicViewModel<T>")]
    [TypeDescriptionProvider(typeof(EditableTypeDescriptorProvider))]
    abstract class ModelBase<T> : IEditableObject, ITransacableObject, IDataErrorInfo, INotifyPropertyChanged //,IDisposable ;有需要再实现
    {
        /// <summary>
        /// T中被用户修改过的属性及修改过的值
        /// </summary>
        private Dictionary<PropertyInfo, object> changedProperties;

        #region ctor
        public ModelBase(T dynamicVM)
        {
            this.ModelInstance = dynamicVM;
            changedProperties = new Dictionary<PropertyInfo, object>();
        }
        #endregion

        #region Properties
        /// <summary>
        /// 被包裹的对象
        /// 借助它可以,使用Expression减少输入错误
        /// </summary>
        protected T ModelInstance { get; private set; }
        /// <summary>
        /// 指示是否用户在编辑时修改了某项属性
        /// </summary>
        public bool HasChanges
        {
            get
            {
                return changedProperties.Count > 0;
            }
        }
        /// <summary>
        /// 指示该实体是新增状态,还是编辑状态
        /// </summary>
        public bool IsEdit { get; protected set; }

        /// <summary>
        /// 该方法的加入是为了满足真实项目中
        /// 在向数据库插入数据;或将服务器提交数据;
        /// 返回成功标记之后;才将修改过的值更新到界面上;
        /// 所以我们需要一个wrappedObject的副本;将修改的值赋给该副本
        /// 然后将该副本提交给数据库或服务端;;;UnderneathModel必须被标记为serializable
        /// </summary>
        /// <returns></returns>
        protected T UnderneathModelCopyContainChangedValue
        {
            get
            {
                T cloneObject = ModelInstance.DeepClone();
                EndEditImpl(cloneObject, false);
                return cloneObject;
            }
        }
        #endregion

        #region  Help Methods
        // 用于从T类型中获取参数propName指示的属性的值
        /// <summary>
        /// 作验证的时候;一律将需要验证的属性的TProperty设置为string;
        /// 其它作为基准的属性类型依然设置为原有类型
        /// </summary>
        protected TProperty GetPropertyValue<TProperty>(string propName)
        {
            var propInfo = typeof(T).GetProperty(propName);
            var PropertyInfo4PropertyInVM = this.GetType().GetProperty(propName);
            if (PropertyInfo4PropertyInVM != null) //优先读取VM上的属性值
            {
                return GetPropValInEntity<TProperty>(this, PropertyInfo4PropertyInVM);
            }
            else
            {
                if (propInfo != null) //如果该属性位于model中
                {
                    return GetPropValInEntity<TProperty>(ModelInstance, propInfo);
                }
                else
                {
                    return default(TProperty);
                }
            }
        }

        TProperty GetPropValInEntity<TProperty>(object entityContainProperty, PropertyInfo propertyInfo4PropertyInEntity)
        {
            if (IsPropInfoBeChanged(propertyInfo4PropertyInEntity))
            {
                return GetPropertyValWhichHasChanged<TProperty>(propertyInfo4PropertyInEntity);
            }
            else //如果该属性值未被修改过
            {
                return GetPropertyValFromObj<TProperty>(entityContainProperty, propertyInfo4PropertyInEntity);
            }
        }

        bool IsPropInfoBeChanged(PropertyInfo propInfoMayChanged)
        {
            return changedProperties.ContainsKey(propInfoMayChanged);
        }

        TProperty GetPropertyValWhichHasChanged<TProperty>(PropertyInfo propertyInfoHasChanged)
        {
            return (TProperty)Convert.ChangeType(changedProperties[propertyInfoHasChanged], typeof(TProperty));
        }

        TProperty GetPropertyValFromObj<TProperty>(object objContainProperty, PropertyInfo propInfo, object[] indexs = null)
        {
            var val = propInfo.GetValue(objContainProperty, indexs);
            return (TProperty)Convert.ChangeType(val == null ? string.Empty : val.ToString(), typeof(TProperty));
        }



        #endregion

        #region IEditableObject Members

        public void BeginEdit()
        {
            changedProperties.Clear();
        }

        /// <summary>
        /// 取消编辑界面的变化
        /// </summary>
        public void CancelEdit()
        {
            var properties = new PropertyInfo[changedProperties.Keys.Count];
            changedProperties.Keys.CopyTo(properties, 0);

            changedProperties.Clear();

            foreach (var property in properties) //让编辑界面上的属性值归位
            {
                RaisePropertyChanged(property.Name);
            }
        }

        //该方法被调用时;代表changedProperties存储的都是合法值;即值类型不会出现无法转换的问题
        public void EndEdit()
        {
            EndEditImpl(ModelInstance, true);
        }

        //DataGrid在行编辑模式下,,,如果验证通过,在退出编辑模式时会调用这个函数...原因已查明
        //能执行这个;说明所有的数据都已经至少可以正确转换
        /// <param name="notify">为false;代表是要获取传递给数据库或服务端的数据...为true;则代表需要更新本地值来反应到UI上</param>
        void EndEditImpl(T wrappedObject, bool notify)
        {
            var propInGenericType = changedProperties.Where(propertyValue => propertyValue.Key.DeclaringType == typeof(T));
            foreach (var propertyValue in propInGenericType)
            {
                if (propertyValue.Key.PropertyType.IsDigit()) //如果为数值类型
                {
                    propertyValue.Key.SetValue(wrappedObject, Convert.ChangeType(propertyValue.Value, propertyValue.Key.PropertyType), null);
                }
                else
                {
                    propertyValue.Key.SetValue(wrappedObject, propertyValue.Value, null);
                }
            }

            if (notify)
            {
                foreach (var propertyValue in propInGenericType)
                {
                    RaisePropertyChanged(propertyValue.Key.Name);
                }
            }

            changedProperties.Clear();
        }


        #endregion

        #region  ITransacableObject Members ;Explicit  Implement ;使用接口分离对象和行为
        //该方法在TransacablePropertyDescriptor类中的GetValue()方法中被调用
        //说明xxxxx
        object ITransacableObject.ReadPropertyValue(PropertyInfo property)
        {
            if (changedProperties.ContainsKey(property))
            {
                return changedProperties[property];
            }
            else
            {
                if (property.DeclaringType == GetType())
                {
                    return property.GetValue(this, null);
                }
                else if (property.DeclaringType == typeof(T))
                {
                    return property.GetValue(ModelInstance, null);
                }
                else
                {
                    throw new InvalidOperationException(
                           string.Format("属性{0}即不属于泛型ModelBase<>，也没有在{1}里面定义", property.Name, typeof(T)));
                }
            }
        }
        //该方法在TransacablePropertyDescriptor类中的SetValue()方法中被调用
        //说明xxxxx
        void ITransacableObject.SetPropertyValue(PropertyInfo property, object value)
        {
            changedProperties[property] = value;
        }

        #endregion

        #region  IDataErrorInfo Members
        public string Error
        {
            get
            {
                string totalError = string.Empty;
                Array.ForEach(ModelInstance.GetType().GetProperties(), item => totalError += this[item.Name]);
                return totalError;
            }
        }

        public string this[string columnName]
        {
            get { return ValidateProperty(columnName); }
        }
        #endregion

        #region abstract method ; provide to subclass for  IDataErrorInfo Interface
        protected abstract string ValidateProperty(string prop);
        #endregion

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        protected void RaisePropertyChanged(string propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        protected bool Set<TProperty>(string propertyName, ref TProperty field, TProperty value)
        {
            if (EqualityComparer<TProperty>.Default.Equals(field, value))
            {
                return false;
            }

            field = value;

            RaisePropertyChanged(propertyName);

            return true;
        }
        #endregion

        #region IDisposable Members
        /* 先留着;以后用
        /// <summary>
        /// Invoked when this object is being removed from the application
        /// and will be subject to garbage collection.
        /// </summary>
        public void Dispose()
        {
            OnDispose();
        }

        /// <summary>
        /// Child classes can override this method to perform 
        /// clean-up logic, such as removing event handlers.
        /// </summary>
        protected virtual void OnDispose()
        {
        }

#if DEBUG
        /// <summary>
        /// Useful for ensuring that ViewModel objects are properly garbage collected.
        /// </summary>
        ~ModelBase()
        {
            //string msg = string.Format("{0} ({1}) ({2}) Finalized", GetType().Name, DisplayName, GetHashCode());
            //System.Diagnostics.Debug.WriteLine(msg);
        }
#endif
*/
        #endregion // IDisposable Members
    }
}
